/*******************************************************************************
*
* (c) Copyright 2014 Freescale Semiconductor
*
****************************************************************************//*!
*
* @file     MCG.c
*
* @author   Salvador Zendejas
*
* @version  0.0.1
*
* @date     Aug 22, 2014
*
* @brief    Multipurpose Clock Generator for Kinetis K.
*
*******************************************************************************/

/*******************************************************************************
* Includes
*******************************************************************************/
#include "MCG.h" /* Include peripheral declarations */

/*******************************************************************************
* Global variables
*******************************************************************************/


/*******************************************************************************
* Local variables
*******************************************************************************/
static sModeCloking_t k64ClkMode;

/*******************************************************************************
* Constants
*******************************************************************************/

/*******************************************************************************
* Global functions
*******************************************************************************/

void vfn_MCG_Init(void){

	#ifdef _Crystal_
	/* Enabling external Clk (Extal0)*/
	OSC_CR = (1 << OSC_CR_ERCLKEN_SHIFT);
	#endif
	
	k64ClkMode.sActualMode = e_FEI;
	k64ClkMode.sWhishedMode = e_PEE;

	while (k64ClkMode.sActualMode != k64ClkMode.sWhishedMode){
		v_fn_MCG_Modetransition(k64ClkMode.sActualMode, k64ClkMode.sWhishedMode);
	}
}
/*******************************************************************************
* Local functions (static)
*******************************************************************************/

/*******************************************************************************
*
* Function: void v_fn_MCG_Modetransition(eModeCLK_t)
*
* Description:  This function implements the State Machine checking what is the Actual state and what it is the possible
* 		next status could change to improve the whished mode
*
*******************************************************************************/
static void v_fn_MCG_Modetransition(eModeCLK_t eModeCLK_Actual, eModeCLK_t eModeCLK_whished){
	switch (eModeCLK_Actual) {
		
		case e_FEI:
			if (e_FEE == eModeCLK_whished) \
				/*Transition to FEE*/
				vfn_FEE();
			else if (e_FBI == eModeCLK_whished || e_BLPI == eModeCLK_whished) \
				/*Transition to FBI*/
				vfn_FBI();
			else \
				/*Transition to FBE or other*/
				vfn_FBE();
			break;
		
		case e_FBI:
			if (e_BLPI == eModeCLK_whished) \
				/*Transition to BLPI*/
				vfn_BLPI();
			break;
		
		case e_FBE:
			if (e_BLPE == eModeCLK_whished) \
				/*Transition to BLPE*/
				vfn_BLPE();
			else vfn_PBE();
			break;
		
		case e_PBE:
			vfn_PEE();
			break;
			
		default:
			
			break;
	}
}
/*******************************************************************************
*
* Function: void vfn_FEI(void)
*
* Description:  MCGOUTCLK is derived from the FLL clk that is controlled by the 32kHz
* 			Internal Ref Clk (IRC). The FLL loop will lock the DCO freq to the FLL factor
* 			as selected by  DRST_DRS and DMX32 (MCG_C4) bits, the internal ref freq
* 			
* 			FLL Engaged Internal
*
*******************************************************************************/
static void vfn_FEI(void){
	MCG_C1 = (MCG_C1_CLKS(CLEAR)) | (SET << MCG_C1_IREFS_SHIFT);
	MCG_C6 &= (!MCG_C6_PLLS_MASK);
	/*Waiting external reference is not the current source for the reference clock*/
	while (!(MCG_S & MCG_S_IREFST_MASK));
	k64ClkMode.sActualMode = e_FEI;
}

/*******************************************************************************
*
* Function: void vfn_FEE(void)
*
* Description:  MCGOUTCLK is derived from the FLL clock (DCOCLK) that is controlled by the
*			external reference clock. The FLL loop will lock the DCO frequency to the FLL factor, 
*			as selected by C4[DRST_DRS] and C4[DMX32] bits, times the external reference frequency, 
*			as specified by C1[FRDIV] and C2[RANGE]. See the C4[DMX32] bit description for more details. 
*			In FEE mode, the PLL is disabled in a low-power state unless C5[PLLCLKEN] is set .
* 			
* 			FLL Engaged External
*
*******************************************************************************/
static void vfn_FEE(void){
	MCG_C1 = (MCG_C1_CLKS(CLEAR)) | (FRDIV << MCG_C1_FRDIV_SHIFT) | (CLEAR << MCG_C1_IREFS_SHIFT);
	MCG_C6 &= (!MCG_C6_PLLS_MASK);
	/*Waiting external reference is the current source for the reference clock*/
	while (MCG_S & MCG_S_IREFST_MASK);
	k64ClkMode.sActualMode = e_FEE;
}

/*******************************************************************************
*
* Function: void vfn_FBI(void)
*
* Description:  MCGOUTCLK is derived either from the slow (32 kHz IRC) or fast (4 MHz IRC)
* 			internal reference clock, as selected by the C2[IRCS] bit. The FLL is operational but 
* 			its output is not used. This mode is useful to allow the FLL to acquire its target frequency 
* 			while the MCGOUTCLK is driven from the C2[IRCS] selected internal reference clock. 
* 			The FLL clock (DCOCLK) is controlled by the slow internal reference clock, 
* 			and the DCO clock frequency locks to a multiplication factor, as selected by C4[DRST_DRS] 
* 			and C4[DMX32] bits, times the internal reference frequency. See the C4[DMX32] bit description
* 			for more details. In FBI mode, the PLL is disabled in a low-power state unless C5[PLLCLKEN] is set .
* 			
* 			FLL Bypassed Internal
*
*******************************************************************************/
static void vfn_FBI(void){
	
	MCG_C2 = (RANGE0 << MCG_C2_RANGE0_SHIFT) | (CLEAR << MCG_C2_HGO0_SHIFT) | (CLEAR << MCG_C2_EREFS0_SHIFT) | (CLEAR << MCG_C2_LP_SHIFT);	
	/*Internal clk, Factor 1280 (50MHz/1280==39.068KHz), Internal clk (FLL)  */
	MCG_C1 = (CLKS_bitfield << MCG_C1_CLKS_SHIFT) | (FRDIV << MCG_C1_FRDIV_SHIFT) | (SET << MCG_C1_IREFS_SHIFT);
	/*Waiting external reference is not the current source for the reference clock*/
	while (!(MCG_S & MCG_S_IREFST_MASK));
	MCG_C6 = CLEAR << MCG_C6_PLLS_SHIFT;
	k64ClkMode.sActualMode = e_FBI;
}

/*******************************************************************************
*
* Function: void vfn_FBE(void)
*
* Description:  MCGOUTCLK is derived from the OSCSEL external reference clock. The FLL is
* 			operational but its output is not used. This mode is useful to allow the FLL to acquire its target
* 			frequency while the MCGOUTCLK is driven from the external reference clock. The FLL clock
* 			(DCOCLK) is controlled by the external reference clock, and the DCO clock frequency locks to a
* 			multiplication factor, as selected by C4[DRST_DRS] and C4[DMX32] bits, times the divided external
* 			reference frequency. See the C4[DMX32] bit description for more details. In FBI mode, the PLL is
* 			disabled in a low-power state unless C5[PLLCLKEN] is set .
* 			
* 			FLL Bypassed External
*
*******************************************************************************/
static void vfn_FBE(void){
	/*Osc to High Freq (3MHz to 8MHz, High Gain, External Osc */
	MCG_C2 = (RANGE0 << MCG_C2_RANGE0_SHIFT) | (SET << MCG_C2_HGO0_SHIFT) | (SET << MCG_C2_EREFS0_SHIFT) | (CLEAR << MCG_C2_LP_SHIFT);
	/*External clk, Factor 1280 (50MHz/1280==39.068KHz), External clk (FLL)  */
	MCG_C1 = (CLKS_bitfield << MCG_C1_CLKS_SHIFT) | (FRDIV << MCG_C1_FRDIV_SHIFT) | (CLEAR << MCG_C1_IREFS_SHIFT);
	/*Waiting until the External Clk has been initialized*/
	while (!(MCG_S & MCG_S_OSCINIT0_MASK));
	/*Waiting external reference is the current source for the reference clock*/
	while (MCG_S & MCG_S_IREFST_MASK);
	/*Waiting the external reference clock is selected to feed MCGOUTCLK*/
	while ((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT != 2);
	/*Select PRDIV = 19 + 1 = 20 (50/20 = 2.5MHz)*/
	MCG_C5 = PRDIV << MCG_C5_PRDIV0_SHIFT;
	MCG_C6 = CLEAR << MCG_C6_PLLS_SHIFT;
	k64ClkMode.sActualMode = e_FBE;
}

/*******************************************************************************
*
* Function: void vfn_PBE(void)
*
* Description:  MCGOUTCLK is derived from the output of PLL which is controlled by a external
* 			reference clock. The PLL clock frequency locks to a multiplication factor, 
* 			as specified by its corresponding VDIV, times the selected PLL reference frequency, 
* 			as specified by its corresponding PRDIV. The PLL's programmable reference divider
* 			must be configured to produce a valid PLL reference clock. 
* 			The FLL is disabled in a low-power state.
* 			
* 			PLL Bypassed External
*
*******************************************************************************/
static void vfn_PBE(void){
	/*Transition to PBE mode*/
	/*External clk, External clk (FLL)  */
	MCG_C1 = (CLKS_bitfield << MCG_C1_CLKS_SHIFT) | (CLEAR << MCG_C1_IREFS_SHIFT);
	MCG_C6 = (MCG_C6_VDIV0(VIDV)) | (SET << MCG_C6_PLLS_SHIFT);
	MCG_C2 = (CLEAR << MCG_C2_LP_SHIFT);
	/*Waiting external reference is the current source for the reference clock*/
	while (MCG_S & MCG_S_IREFST_MASK);
	while (!(MCG_S & MCG_S_PLLST_MASK));	/*Waiting that the current source for Clk is PLL*/
	while (!(MCG_S & MCG_S_LOCK0_MASK)); 	/*Waiting to PLL lock*/
	k64ClkMode.sActualMode = e_PBE;
}

/*******************************************************************************
*
* Function: void vfn_PEE(void)
*
* Description:  MCGOUTCLK is derived from the output of PLL which is controlled by a external
* 			reference clock. The PLL clock frequency locks to a multiplication factor, 
* 			as specified by its corresponding VDIV, times the selected PLL reference frequency, 
* 			as specified by its corresponding PRDIV. The PLL's programmable reference divider 
* 			must be configured to produce a valid PLL reference clock. 
* 			The FLL is disabled in a low-power state
* 			
* 			PLL Engaged External
*
*******************************************************************************/
static void vfn_PEE(void){
	/*Transition to PEE mode*/
	/*Selecting the output of the PLL as the system clock source*/
	MCG_C1 = (CLEAR << MCG_C1_CLKS_SHIFT) | (CLEAR << MCG_C1_IREFS_SHIFT);
	/*Waiting external reference is the current source for the reference clock*/
	while (MCG_S & MCG_S_IREFST_MASK);
	/*Select the PLL*/
	MCG_C6 = (SET << MCG_C6_PLLS_SHIFT);
	while (!(MCG_S & MCG_S_PLLST_MASK));	/*Waiting that the current source for Clk is PLL*/
	while ((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT != 3);
	k64ClkMode.sActualMode = e_PEE;
	/*Finish of configure to 120MHz*/
}

/*******************************************************************************
*
* Function: void vfn_BLPE(void)
*
* Description:  MCGOUTCLK is derived from the OSCSEL external reference clock.
* 			The FLL is disabled and PLL is disabled even if the C5[PLLCLKEN] is set to 1
* 			
* 			Bypassed Low Power External
*
*******************************************************************************/
static void vfn_BLPE(void){
	
}

/*******************************************************************************
*
* Function: void vfn_BLPI(void)
*
* Description:  MCGOUTCLK is derived from the internal reference clock. 
* 			The FLL is disabled and PLL is disabled even if C5[PLLCLKEN] is set to 1.
* 			
* 			Bypassed Low Power Internal
*
*******************************************************************************/
static void vfn_BLPI(void){
	
}
/*******************************************************************************
* ISR functions
*******************************************************************************/

